package org.skydiveapplibrary.util;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

import com.dropbox.client2.DropboxAPI;
import com.dropbox.client2.DropboxAPI.DropboxFileInfo;
import com.dropbox.client2.DropboxAPI.Entry;
import com.dropbox.client2.ProgressListener;
import com.dropbox.client2.android.AndroidAuthSession;
import com.dropbox.client2.exception.DropboxException;
import com.dropbox.client2.session.AccessTokenPair;
import com.dropbox.client2.session.AppKeyPair;
import com.dropbox.client2.session.Session.AccessType;
import com.dropbox.client2.session.TokenPair;

import android.app.ProgressDialog;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;

public class DropBoxClient
{
    // oauth keys
    private static final String APP_KEY = "fcumyczh0enwngw";
    private static final String APP_SECRET = "qth1zfxdfqwr3tw";

    // access type
    private static final AccessType ACCESS_TYPE = AccessType.DROPBOX;
    // account oauth keys
    private static final String ACCOUNT_PREFERENCES_NAME = "preferences";
    private static final String ACCESS_KEY_NAME = "ACCESS_KEY";
    private static final String ACCESS_SECRET_NAME = "ACCESS_SECRET";

    // context
    private Context _context;
    
    // dialog
    private ProgressDialog mProgressDialog = null;
    
    // progress listener
    private ProgressListener _progressListener = new ProgressListener()
    {
        @Override
        public long progressInterval()
        {
        	// update progess every .1 seconds
            return 100;
        }

        @Override
        public void onProgress(long bytes, long total)
        {
        	// show percentage
        	int percent = (int)(100.0*(double)bytes/total);
        	mProgressDialog.setProgress(percent);
        }
    };

    // the api
    private DropboxAPI<AndroidAuthSession> _dropboxApi;

    public DropBoxClient(Context context)
    {
        _context = context;
        
        // create the Dropbox API.
        AndroidAuthSession session = buildSession();
        _dropboxApi = new DropboxAPI<AndroidAuthSession>(session);
    }

    public boolean isAuthenticated()
    {
        return _dropboxApi.getSession().isLinked();
    }

    public void startAuthentication()
    {
        _dropboxApi.getSession().startAuthentication(_context);
    }
    
    public boolean completeAuthentication()
    {
        if (_dropboxApi.getSession().authenticationSuccessful())
        {
            _dropboxApi.getSession().finishAuthentication();

            // Store it locally in our app for later use
            TokenPair tokens = _dropboxApi.getSession().getAccessTokenPair();
            storeTokens(tokens.key, tokens.secret);
            
            return true;
        }
        
        return false;
    }

    public void logout()
    {
        // Remove credentials from the session
        _dropboxApi.getSession().unlink();

        // Clear our stored keys
        clearTokens();
    }
    
    public void uploadFile(String remoteFolder, File file, ProgressDialog progressDialog)
    	throws DropboxException, FileNotFoundException
    {
    	mProgressDialog = progressDialog;
    	
    	// create folder if it doesn't exist
    	if (!rootFolderExists(remoteFolder))
    	{
    		_dropboxApi.createFolder("/" + remoteFolder);
    	}
    	
    	// get file input stream
    	FileInputStream fis = new FileInputStream(file);
    	
    	// do upload
    	_dropboxApi.putFileOverwrite("/" + remoteFolder + "/" + file.getName(),
    		fis, file.length(), _progressListener);
    	
    	mProgressDialog.setProgress(100);
    }
    
    public Entry getFolderMetadata(String remoteFolder)
    		throws DropboxException
    {
    	// get remote path
    	String remotePath = "/" + remoteFolder + "/";
    	
    	return _dropboxApi.metadata(remotePath, 100, null, true, null);
    }
    
    public boolean fileExists(Entry entries, String remoteFileName)
    		throws DropboxException
    {
    	for (Entry e : entries.contents) {
    	    if (!e.isDeleted)
    	    {
    	        if (e.fileName().equals(remoteFileName))
    	        	return true;       	
    	    }
    	}

    	return false;
    }

    public boolean getFile(String remoteFolder, String remoteFileName, File localFile, ProgressDialog progressDialog)
    	throws IOException, DropboxException
    {
    	mProgressDialog = progressDialog;
    	
    	// get remote path
    	String remotePath = "/" + remoteFolder + "/" + remoteFileName;
    	
    	OutputStream outputStream = null;
    	try
    	{
    		outputStream = new BufferedOutputStream(new FileOutputStream(localFile));
    		DropboxFileInfo fileInfo = 
    			_dropboxApi.getFile(remotePath, null, outputStream, _progressListener);
    		
    		// return true if file found
    		return fileInfo != null;
    	}
    	finally
    	{
    		if (outputStream != null)
    			outputStream.close();
    		mProgressDialog.setProgress(100);
    	}
    }
    
    private boolean rootFolderExists(String remoteFolder)
    	throws DropboxException
    {
    	Entry rootEntry = _dropboxApi.metadata("", 0, null, true, null);
    	for (Entry entry: rootEntry.contents)
    	{
    		if (entry.isDeleted || !entry.isDir)
    			continue;
    		
    		if (entry.path.endsWith(remoteFolder))
    			return true;
    	}
    	
    	return false;
    }
    
    private AndroidAuthSession buildSession()
    {
        AppKeyPair appKeyPair = new AppKeyPair(APP_KEY, APP_SECRET);
        AndroidAuthSession session;

        // get key/secret
        String key = getKey();
        String secret = getSecret();

        // if keys not cached, authentication required
        if (key != null && secret != null)
        {
            AccessTokenPair accessToken = new AccessTokenPair(key, secret);
            session = new AndroidAuthSession(appKeyPair, ACCESS_TYPE, accessToken);
        }
        else
        {
            session = new AndroidAuthSession(appKeyPair, ACCESS_TYPE);
        }

        return session;
    }

    private String getKey()
    {
        // get from shared preferences
        SharedPreferences preferences = _context.getSharedPreferences(ACCOUNT_PREFERENCES_NAME, 0);
        return preferences.getString(ACCESS_KEY_NAME, null);
    }

    private String getSecret()
    {
        // get from shared preferences
        SharedPreferences preferences = _context.getSharedPreferences(ACCOUNT_PREFERENCES_NAME, 0);
        return preferences.getString(ACCESS_SECRET_NAME, null);
    }

    private void storeTokens(String key, String secret)
    {
        // Save the access key for later
        SharedPreferences preferences = _context.getSharedPreferences(ACCOUNT_PREFERENCES_NAME, 0);
        Editor edit = preferences.edit();
        edit.putString(ACCESS_KEY_NAME, key);
        edit.putString(ACCESS_SECRET_NAME, secret);
        edit.commit();
    }

    private void clearTokens()
    {
        SharedPreferences preferences = _context.getSharedPreferences(ACCOUNT_PREFERENCES_NAME, 0);
        Editor edit = preferences.edit();
        edit.clear();
        edit.commit();
    }
}
